package com.uxsino.simo.indicator;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.uxsino.commons.utils.config.ConfigProp;
import com.uxsino.commons.utils.config.PropElement;
import com.uxsino.reactorq.model.FieldType;
import com.uxsino.reactorq.model.INDICATOR_TYPE;
import com.uxsino.reactorq.model.IndicatorValue;
import com.uxsino.simo.networkentity.EntityInfo;
import com.uxsino.simo.query.QueryContext;
import com.uxsino.simo.utils.ConfigLoadingContext;
import com.uxsino.simo.utils.ConfigPropLoader;

public class AggregateIndicator extends IndicatorWithRefer implements INoneQueryIndicator, ICompound {

    public static final class AggrField extends IndicatorField {

        @ConfigProp(name = "from")
        @JsonProperty("from")
        @JSONField(name = "from")
        public String fromField;

        public AGGREGATE_FUNCTION function;

        public AggrField(String fieldName) {
            super(fieldName, FieldType.NUMBER);
        }

        @ConfigProp(name = "function", required = true, letterCase = ConfigProp.CASE_MODE.UPPER)
        public void setFunction(String functionName) {
            this.function = AGGREGATE_FUNCTION.valueOf(functionName);
        }
    }

    @JsonProperty("fields")
    @JSONField(name = "fields")
    protected Map<String, IIndicatorField> fields = new LinkedHashMap<>();

    private String keyFieldNames;

    private ListAggregateFunction innerFunc = new ListAggregateFunction();

    public AggregateIndicator(String indicatorName) {
        super(indicatorName);
        fields = new LinkedHashMap<String, IIndicatorField>();
    }

    @Override
    public INDICATOR_TYPE getIndicatorType() {
        return INDICATOR_TYPE.AGGREGATE;
    }

    @SuppressWarnings("unchecked")
    public Map<String, Object> apply(QueryContext ctxt, List<IndicatorValue> lists) {
        ArrayList<Map<String, Object>> ml = new ArrayList<>();

        for (IndicatorValue iv : lists) {
            if (iv.value != null) {
                ml.addAll((List<Map<String, Object>>) iv.value);
            }
        }
        return innerFunc.apply(ml);
    }

    // @Override
    public void loadProp(PropElement eInd, ConfigLoadingContext lctxt) {
        ConfigPropLoader loader = new ConfigPropLoader(lctxt);
        PropElement eFields = eInd.getFirstElement("fields");

        if (eFields == null) {
            lctxt.error(eInd.getSourceLocation(), "fields not found.");
            return;
        }

        for (PropElement efld : eFields.getElements("field")) {

            String fldName = efld.getProp("name").trim();
            AggrField fld = new AggrField(fldName);
            try {
                loader.loadProperties(fld, efld);
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                lctxt.error(efld.getSourceLocation(), "error loading field {}", fldName);
            }

            innerFunc.addField(fld.fromField, fld.name, fld.function);
            fields.put(fld.name, fld);
        }
    }

    @Override
    public Iterator<Entry<String, IIndicatorField>> getFieldIterator() {
        return fields.entrySet().iterator();
    }

    @Override
    public int getFieldsCount() {
        return fields.size();
    }

    @Override
    public IIndicatorField getField(String fieldName) {
        return fields.get(fieldName);
    }

    @Override
    public Collection<IIndicatorField> getFieldsCollection() {
        return fields.values();
    }

    @Override
    public IIndicatorField createField(String name, FieldType fieldType) {
        return new AggrField(name);
    }

    @Override
    public String[] getKeyFieldNames() {
        return null == keyFieldNames ? new String[0] : keyFieldNames.split(",");
    }

    @Override
    public void setKeyFieldNames(String names) {
        keyFieldNames = names;
    }

    @Override
    public void doPostQuery(EntityInfo entity, QueryContext ctxt, Object value) {
        long time = 0;
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        for (Indicator ind : getReferedIndicators()) {
            IndicatorValue iv = ctxt.getDepo().getIndicatorValue(entity.id, ind);
            if (iv != null) {
                @SuppressWarnings("unchecked")
                List<Map<String, Object>> v = (List<Map<String, Object>>) (iv.value);
                if (v != null) {
                    list.addAll(v);
                }
                time += iv.cost;
            }
        }
        Map<String, Object> aggr = innerFunc.apply(list);

        ctxt.setIndicatorValue(entity, this, 0, aggr, time);
    }

    @Override
    public boolean isFieldsExtendable() {
        return false;
    }

}
